/************************************************************
*
Program: primes.c
- The program creates 2 threads and calculates
* the prime numbers. The main function takes the upper bound as an
* argument to compute primes within the upper bound range and displays
* all the prime numbers found.
* *********************************************************/

#define SAMPLE_USE_MUTEX 2

#ifdef SAMPLE_USE_MUTEX
#pragma region "Sample use mutex"

extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <windows.h>
#include <process.h>


HANDLE  g_hMutex	 = NULL;
int		*g_PrimeArr  = NULL;
int		g_primeMax   = 0;
int		g_primeIndex = 3;

DWORD WINAPI ComputePrimes (LPVOID);


int main (int argc, char **argv) {
	int		Max = 0;
	HANDLE  hThread1 = NULL;
	HANDLE	hThread2 = NULL;
	int     i, thd1=1,thd2=2;

	if(argc < 2) {
		printf ("Usage error: The program needs the upper bound of search range.\n");
		return 0;
	}

	g_primeMax = Max = atoi(argv[1]);
	if(Max <= 2) {
		printf ("Error: Upper Bound value has to be greater than 2\n");
		return 0;
	}

	//Allocate memory for the main thread's elements and initialize
	g_PrimeArr = (int *) malloc(sizeof (int) * (Max+1));
	for (i = 0; i<=Max; i++) {
		g_PrimeArr[i] = 0;
	}

	//Set the first prime number
	g_PrimeArr[1] = 2;
	//create mutex
	g_hMutex = CreateMutex (NULL, //default security attributes
							TRUE, //initial owner of the mutex
							NULL);
	//name of the mutex.
	if (g_hMutex == NULL) {
		printf ("Error creating Mutex\n");
		return 0;
	}
	else {
		ReleaseMutex(g_hMutex);
	}

	//create 2 threads
	if ( (hThread1 = CreateThread(NULL, 0, ComputePrimes, (LPVOID)thd1, 0, NULL)) == NULL ) {
		printf("Error: Failed to create Thread1\n");
	}
	if ( (hThread2 = CreateThread(NULL, 0, ComputePrimes, (LPVOID)thd2, 0, NULL)) == NULL ) {
		printf("Error: Failed to create Thread2\n");
	}

	// wait for the thread to finish computing
	WaitForSingleObject ( hThread1, //handle for thread
						 INFINITE); //time out interval
	WaitForSingleObject ( hThread2, INFINITE);

	// Print the computed Prime Numbers
	printf ("Displaying primes\n");
	printf ("Primes numbers in the range 1 to %d are: \n", Max);
	printf ("---------------------------------------- \n");
	
	for(i = 0; i <= Max; i++) {
		if(g_PrimeArr[i] != 0) {
			printf("%d ", g_PrimeArr[i]);
		}
	}
	printf("\n");

	//close handles
	CloseHandle (g_hMutex);
	CloseHandle (hThread1);
	CloseHandle (hThread2);

	// free allocated memory
	if(g_PrimeArr) {
		free(g_PrimeArr);
		g_PrimeArr = NULL;
	}

	return 0;
}


/****************************************************************
* ComputePrimes(..): Thread Function used to compute primes		
*****************************************************************/
DWORD WINAPI ComputePrimes(LPVOID idx)
{
	DWORD	dwresult;
	int		currPrime = 0, i, sqroot;
	BOOL	isPrime = TRUE;

	while (TRUE) {
		isPrime = TRUE;
		
		if (g_primeIndex <= g_primeMax) {
			dwresult = WaitForSingleObject (g_hMutex, INFINITE);

			if (dwresult == WAIT_OBJECT_0) {
				//if mutex acquired is successful
				currPrime = g_primeIndex;
				g_primeIndex++;
				//release the mutex
				ReleaseMutex (g_hMutex);
			}//End if

			sqroot = (int)sqrt((double)currPrime) + 1;
			printf ("Thread %d checking if number %d is prime\n", (int)idx, currPrime);

			for (i = 2; i<= sqroot; i++) {
				if((currPrime%i) == 0) {
					isPrime = FALSE;
					break;
				}
			}//End for

			dwresult = WaitForSingleObject (g_hMutex, INFINITE);
			if (dwresult == WAIT_OBJECT_0) {
				if(isPrime)
				g_PrimeArr[currPrime] = currPrime;
				ReleaseMutex (g_hMutex);
			}

		}
		else {
			return 0;
		}

	}//End while

	return 0;
} 
}

#pragma endregion
#endif
